小编典典

在 JavaFX (JPA Repo, Service) 中添加 Spring 依赖注入

all

我有一个 java FX 基本应用程序,它有一个简单的场景(一个表单)。我有一个 Mysql Db,我正在使用 Spring JPA(spring data jpa 即存储库/实体)与之交互。

现在,因为我们知道 javaFx 有一些生命周期钩子,即:init() start() 和 stop()。

假设我想使用 JPA save()方法在数据库中插入数据。通常,如果它是我的控制器,正常的数据库注入,如:

@Autowired
EmployeeRepo employeeRepo;

本来可以的。但是,我无法在生命周期方法中访问它(或任何自动装配注入)。

public void start(Stage primaryStage) throws Exception {

// Some Code

employeeRepo.findAll() <- This is returning null

但是,当我添加测试方法并使用相同的方法时,它可以正常工作:

@PostConstruct
public void test() {
// Repo object is not giving null
}

有没有办法可以在按钮侦听器中手动注入依赖项或将其传递给启动方法。

请让我知道是否有解决方案,因为我是 JavaFX 新手


阅读 269

收藏
2022-06-16

共1个答案

小编典典

JavaFX 的依赖注入选项

有许多方法可以将依赖项注入到 JavaFX 应用程序中。例如,Gluon 有一个名为Gluon Ignite的项目,它为各种依赖注入框架(如 Guice、Spring 和 Dagger)启用 JavaFX 应用程序。

由于您选择了 Spring 作为依赖注入框架,并且希望使用一系列其他 Spring 工具,例如 Spring Data 存储库,因此您可能希望考虑使用 SpringBoot 应用程序。

您可以使您的 JavaFX 应用程序成为 SpringBoot 应用程序(尽管这对于获取依赖项注入并不是绝对必要的),以便在您的应用程序中获得一堆可用的 Spring 工具。

该示例的关键部分是应用程序的 init() 方法(我刚刚复制并粘贴并复制到此处以供参考):

@SpringBootApplication
public class DemoApplication extends Application {

    private ConfigurableApplicationContext springContext;
    private Parent root;

    @Override
    public void init() throws Exception {
        springContext = SpringApplication.run(DemoApplication.class);
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/sample.fxml"));
        fxmlLoader.setControllerFactory(springContext::getBean);
        root = fxmlLoader.load();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Hello World");
        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void stop() throws Exception {
        springContext.stop();
    }


    public static void main(String[] args) {
        launch(DemoApplication.class, args);
    }
}

示例应用程序正在运行 SpringBoot 应用程序以启动 Spring 系统并让它在 init 方法中提供应用程序上下文。然后应用程序使用 FXMLLoadersetControllerFactory()方法允许 Spring 实例化 FXML 控制器并在应用程序中注入对 Spring bean 的引用。

自动连接您的 JavaFX 控制器

要让您的 JAVAFX FXML 控制器自动装配,除了以下调用 FXMLLoader 之外:

fxmlLoader.setControllerFactory(springContext::getBean);

您还需要将您的类注释为 Spring @Component,并且@Autowired在您希望控制器使用的任何 Spring 依赖项中。通过这种方式,FXMLLoader 将基于 @FXML 的引用注入到您的 UI 元素,它还将委托给 spring 上下文以注入 Spring 依赖项。

@Component
public class DemoController {
    @FXML
    private Label usernameLabel; 

    @Autowired
    public SpringService mySpringService;

    public void initialize() {
        usernameLabel.setText(
            mySpringService.getLoggedInUsername()
        );
    }
}

请注意,Spring 有一个@Controller注解,可用于注解 JavaFX 控制器而不是@Component注解,但我建议避免将其@Controller用于此目的,而是将@Controller注解用于 Spring REST 服务端点控制器定义。

Spring Boot 应用程序和 JavaFX 应用程序之间的关注点分离

您可能需要注意的一件事是,运行 SpringBoot 应用程序会生成一个新的应用程序实例,并且您已经拥有一个由 JavaFX 系统启动的 JavaFX 应用程序实例,因此如果 SpringBoot 应用程序会产生两个 JavaFX 应用程序实例和 JavaFX 应用程序基于相同的类(如上所示),这可能会造成混淆。

因此,将 Spring 应用程序和 JavaFX 应用程序分开可能会更好。这增强了应用程序的 UI 和服务部分之间的关注点分离,并使测试更容易,因为 Spring 应用程序可以独立于启动和关闭 JavaFX 应用程序进行单元测试。

自动装配 JavaFX 应用程序类

注意,使用上述设置,它不会自动装配 JavaFX 应用程序类实例化实例。如果您希望这样做,您可以使用下面说明的技术在 JavaFX 实例化的应用程序类中注入 bean:

将以下代码放在应用程序的 init 方法中:

springContext
    .getAutowireCapableBeanFactory()
    .autowireBeanProperties(
        this,
        AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, 
        true
    );

mvvmFX框架使用与上述类似的方法将 SpringBoot 与 JavaFX 应用程序集成:

将命令行参数从 JavaFX 传递到 SpringBoot

要将参数从 JavaFX 应用程序传递到 SpringBoot 应用程序,请使用:

SpringApplication.run(
    DemoApplication.class, 
    getParameters().getRaw().toArray(new String[0])
);

其他问题

如果您需要对 SpringApplication 的启动进行更多控制,您可以使用 SpringApplicationBuilder 例如:

ConfigurableApplicationContext startupContext =
        new SpringApplicationBuilder(DemoApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);

这个答案只是为了给你一些关于如何解决这个问题的提示,而不是作为关于如何将依赖注入与 JavaFX 集成的通用指南,这可能最终成为一个非常棘手的主题,需要全面涵盖。

2022-06-16